<?php
/**
 * Smart Library - Smart Framework
 * http://smart-framework.highet.eu
 *
 * @category   SmartL
 * @package    SmartL_Zend
 * @subpackage Auth
 * @copyright  Copyright (c) 2005-2008 Smart Framework team - Stepan Sindelar, Vojtech Vit
 * @license    http://www.opensource.org/licenses/bsd-license.php	New BSD License
 */

/**
 * @see Zend_Auth_Adapter_Interface
 */
require_once 'Zend/Auth/Adapter/Interface.php';
/**
 * @see Doctrine_Query
 */
require_once 'Doctrine/Query.php';
/**
 * @see Zend_Auth_Result
 */
require_once 'Zend/Auth/Result.php';

/**
 * Zend_Auth adapter for Doctrine records.
 * 
 * @package SmartL_Zend
 * @subpackage Auth
 */
class SmartL_Zend_Auth_Adapter_Doctrine implements Zend_Auth_Adapter_Interface {
	/**
	 * The name of Doctrine_Record to check
	 *
	 * @var string
	 */
	protected $_recordName = null;
	/**
	 * The column to use as the identity
	 *
	 * @var string
	 */
	protected $_identityColumn = null;
	/**
	 * Columns to be used as the credentials
	 *
	 * @var string
	 */
	protected $_credentialColumn = null;
	/**
	 * Identity value
	 *
	 * @var string
	 */
	protected $_identity = null;
	/**
	 * Credential values
	 *
	 * @var string
	 */
	protected $_credential = null;
	/**
	 * 
	 * 
	 * @var array
	 */
	protected $_authenticateResultInfo = null;
	/**
	 * Results of database authentication query
	 *
	 * @var Doctrine_Record
	 */
	protected $_resultRow = null;
	
	/**
	 * Class constructor, sets configuration options.
	 *
	 * @param  string                   $recordName
	 * @param  string                   $identityColumn
	 * @param  string                   $credentialColumn
	 * @param  string                   $credentialTreatment
	 * @todo 11 : Opravdu používat $credentialTreatment.
	 * @return void
	 */
	public function __construct($recordName = null, $identityColumn = null, $credentialColumn = null, $credentialTreatment = null) {
		if (null !== $recordName) {
			$this->setRecordName ( $recordName );
		}
		if (null !== $identityColumn) {
			$this->setIdentityColumn ( $identityColumn );
		}
		if (null !== $credentialColumn) {
			$this->setCredentialColumn ( $credentialColumn );
		}
		if (null !== $credentialTreatment) {
			$this->setCredentialTreatment ( $credentialTreatment );
		}
	}
	/**
	 * Sets the name of record to be used.
	 *
	 * @param  string $recordName Record name
	 * @return SmartL_Zend_Auth_Adapter_Doctrine
	 */
	public function setRecordName($recordName) {
		$this->_recordName = $recordName;
		return $this;
	}
	/**
	 * Sets the column name to be used as the identity column.
	 *
	 * @param  string $identityColumn Identity column name
	 * @return SmartL_Zend_Auth_Adapter_Doctrine
	 */
	public function setIdentityColumn($identityColumn) {
		$this->_identityColumn = $identityColumn;
		return $this;
	}
	/**
	 * Sets the column name to be used as the credential column.
	 *
	 * @param  string $credentialColumn Credential column name
	 * @return SmartL_Zend_Auth_Adapter_Doctrine
	 */
	public function setCredentialColumn($credentialColumn) {
		$this->_credentialColumn = $credentialColumn;
		return $this;
	}
	/**
	 * Sets the value to be used as the identity.
	 *
	 * @param  string $value Identity value
	 * @return SmartL_Zend_Auth_Adapter_Doctrine
	 */
	public function setIdentity($value) {
		$this->_identity = $value;
		return $this;
	}
	/**
	 * Sets the credential value to be used.
	 *
	 * @param  string $credential Credential value
	 * @return SmartL_Zend_Auth_Adapter_Doctrine
	 */
	public function setCredential($credential) {
		$this->_credential = $credential;
		return $this;
	}
	/**
	 * Returns the result as a Doctrine_Record class.
	 * 
	 * @return Doctrine_Record Child of Doctrine_Record that represets logged user. 
	 */
	public function getResultRowObject() {
		if (! $this->_resultRow) {
			return false;
		}
		return $this->_resultRow;
	}
	/**
	 * Defined by Zend_Auth_Adapter_Interface. This method is called to attempt 
	 * an authenication.  Previous to this call, this adapter would have already
	 * been configured with all nessissary information to successfully connect 
	 * to a database table and attempt to find a record matching the provided 
	 * identity.
	 *
	 * @throws SmartL_Zend_Auth_Adapter_Doctrine_Exception if answering the 
	 * authentication query is impossible
	 * @return Zend_Auth_Result
	 */
	public function authenticate() {
		$this->_authenticateSetup ();
		$dbQuery = $this->_authenticateCreateQuery ();
		$resultIdentities = $this->_authenticateExecQuery ( $dbQuery );
		$authResult = $this->_authenticateValidateResult ( $resultIdentities [0] );
		return $authResult;
	}
	/**
	 * This method abstracts the steps involved with making sure that this 
	 * adapter was indeed setup properly with all required peices of information.
	 *
	 * @throws SmartL_Zend_Auth_Adapter_Doctrine_Exception - in the event that 
	 * setup was not done properly
	 * @return true
	 */
	protected function _authenticateSetup() {
		$exception = null;
		if ($this->_recordName == '') {
			$exception = 'A record name must be supplied for the SmartL_Zend_Auth_Adapter_Doctrine authentication adapter.';
		} elseif ($this->_identityColumn == '') {
			$exception = 'An identity column must be supplied for the SmartL_Zend_Auth_Adapter_Doctrine authentication adapter.';
		} elseif ($this->_credentialColumn == '') {
			$exception = 'A credential column must be supplied for the SmartL_Zend_Auth_Adapter_Doctrine authentication adapter.';
		} elseif ($this->_identity == '') {
			$exception = 'A value for the identity was not provided prior to authentication with SmartL_Zend_Auth_Adapter_Doctrine.';
		} elseif ($this->_credential === null) {
			$exception = 'A credential value was not provided prior to authentication with SmartL_Zend_Auth_Adapter_Doctrine.';
		}
		if (null !== $exception) {
			/**
			 * @see SmartL_Zend_Auth_Adapter_Doctrine_Exception
			 */
			require_once 'SmartL/Zend/Auth/Adapter/Doctrine/Exception.php';
			throw new SmartL_Zend_Auth_Adapter_Doctrine_Exception ( $exception );
		}
		$this->_authenticateResultInfo = array ('code' => Zend_Auth_Result::FAILURE, 'identity' => $this->_identity, 'messages' => array () );
		return true;
	}
	/**
	 * This method creates a Doctrine_Query object that is completely configured 
	 * to be queried against the database.
	 *
	 * @return Doctrine_Query
	 */
	protected function _authenticateCreateQuery() {
		$query = new Doctrine_Query ( );
		$query->from ( $this->_recordName )->select ( "*" )->where ( "{$this->_identityColumn} = ?", $this->_identity );
		return $query;
	}
	/**
	 * This method accepts a Doctrine_Query object and performs a query against 
	 * the database with that object.
	 *
	 * @param Doctrine_Query $dbQuery
	 * @throws SmartL_Zend_Auth_Adapter_Doctrine_Exception - when a invalid query 
	 * object is encoutered
	 * @return array
	 */
	protected function _authenticateExecQuery(Doctrine_Query $dbQuery) {
		try {
			$resultIdentities = $dbQuery->execute ();
		} catch ( Exception $e ) {
			/**
			 * @see SmartL_Zend_Auth_Adapter_Doctrine_Exception
			 */
			require_once 'SmartL/Zend/Auth/Adapter/Doctrine/Exception.php';
			throw new SmartL_Zend_Auth_Adapter_Doctrine_Exception ( 'The supplied parameters to SmartL_Zend_Auth_Adapter_Doctrine failed to ' . 'produce a valid sql statement, please check record and column names ' . 'for validity. Error message: ' . $e->getMessage () );
		}
		if (is_array ( $resultIdentities ) and ($authResult = $this->_authenticateValidateResultset ( $resultIdentities )) instanceof Zend_Auth_Result) {
			return $authResult;
		}
		$someResultIdentity = $resultIdentities [0];
		// Check if credential column exists        
		if (! isset ( $someResultIdentity [$this->_credentialColumn] )) {
			/**
			 * @see SmartL_Zend_Auth_Adapter_Doctrine_Exception
			 */
			require_once 'SmartL/Zend/Auth/Adapter/Doctrine/Exception.php';
			throw new SmartL_Zend_Auth_Adapter_Doctrine_Exception ( 'The supplied parameters to SmartL_Zend_Auth_Adapter_Doctrine failed to ' . 'produce a valid sql statement, please check record and column names ' . 'for validity.' );
		}
		return $resultIdentities;
	}
	/**
	 * This method attempts to make certian that only one record was returned 
	 * in the result set.
	 *
	 * @param array $resultIdentities
	 * @return true|Zend_Auth_Result
	 */
	protected function _authenticateValidateResultSet(Array $resultIdentities) {
		if (count ( $resultIdentities ) < 1) {
			$this->_authenticateResultInfo ['code'] = Zend_Auth_Result::FAILURE_IDENTITY_NOT_FOUND;
			$this->_authenticateResultInfo ['messages'] [] = 'A record with the supplied identity could not be found.';
			return $this->_authenticateCreateAuthResult ();
		} elseif (count ( $resultIdentities ) > 1) {
			$this->_authenticateResultInfo ['code'] = Zend_Auth_Result::FAILURE_IDENTITY_AMBIGUOUS;
			$this->_authenticateResultInfo ['messages'] [] = 'More than one record matches the supplied identity.';
			return $this->_authenticateCreateAuthResult ();
		}
		return true;
	}
	/**
	 * This method attempts to validate that the record in the result set is 
	 * indeed a record that matched the identity provided to this adapter.
	 *
	 * @param array $resultIdentity
	 * @return Zend_Auth_Result
	 */
	protected function _authenticateValidateResult($resultIdentity) {
		$credentialColumn = $this->_credentialColumn;
		
		if ($resultIdentity->$credentialColumn !== $this->_credential) {
			$this->_authenticateResultInfo ['code'] = Zend_Auth_Result::FAILURE_CREDENTIAL_INVALID;
			$this->_authenticateResultInfo ['messages'] [] = 'Supplied credential is invalid.';
			return $this->_authenticateCreateAuthResult ();
		}
		$this->_resultRow = $resultIdentity;
		$this->_authenticateResultInfo ['code'] = Zend_Auth_Result::SUCCESS;
		$this->_authenticateResultInfo ['messages'] [] = 'Authentication successful.';
		return $this->_authenticateCreateAuthResult ();
	}
	/**
	 * This method creates a Zend_Auth_Result object from the information that 
	 * has been collected during the authenticate() attempt.
	 *
	 * @return Zend_Auth_Result
	 */
	protected function _authenticateCreateAuthResult() {
		return new Zend_Auth_Result ( $this->_authenticateResultInfo ['code'], $this->_authenticateResultInfo ['identity'], $this->_authenticateResultInfo ['messages'] );
	}
}